/*
 * table_CopyGridRow button OnClickFunc 参数{ids:ids,data:data,tr:tr,gridId:'{Id}'}
 * 
 * 生命周期
 * 1.ListVM先在后端实例化
 * 2.wt:grid实例化，并关联listvm
 * 3.
 * 
 */

using Microsoft.AspNetCore.Mvc.ViewFeatures;
using Microsoft.AspNetCore.Razor.TagHelpers;
using System;
using System.Collections.Generic;
using System.Data;
using System.Linq;
using System.Text;
using System.Text.Json;
using System.Text.RegularExpressions;
using System.Xml;
using WalkingTec.Mvvm.Core;
using WalkingTec.Mvvm.Core.Extensions;

namespace ReaWTM.LayuiHelper
{
	[HtmlTargetElement("wt:grid", Attributes = REQUIRED_ATTR_NAME, TagStructure = TagStructure.WithoutEndTag)]
	public class DataTableTagHelper : BaseTag
	{
		/// <summary>
		/// 前端分页
		/// </summary>
		public bool? IsNeedPage { get; set; }
		public DataTableTagHelper()
		{
			_layuiCols = new List<List<LayuiColumn>>();
		}

		/// <summary>
		/// 用于自动生成的 GridId 的前缀
		/// </summary>
		public const string TABLE_ID_PREFIX = "wtTable_";

		/// <summary>
		/// 用于存储 DataTable render后返回的table变量的前缀
		/// </summary>
		public const string TABLE_JSVAR_PREFIX = "wtVar_";

		/// <summary>
		/// 用于生成操作列
		/// </summary>
		public const string TABLE_TOOLBAR_PREFIX = "wtToolBar_";

		protected const string REQUIRED_ATTR_NAME = "vm";

		/// <summary>
		/// 排除的搜索条件
		/// </summary>
		private static readonly string[] _excludeParams = {
			"Page",
			"Limit",
			"Count",
			"PageCount",
			"FC",
			"DC",
			"VMFullName",
			"SortInfo",
			"TreeMode",
			"IsPostBack",
			"DC",
			"LoginUserInfo",
			"MSD",
			"Session",
			"Wtm",
			"ViewDivId"
		};

		/// <summary>
		/// 排除的搜索条件类型，搜索条件数据源可能会存储在Searcher对象中
		/// </summary>
		private static readonly Type[] _excludeTypes = {
			typeof(List<ComboSelectListItem>),
			typeof(ComboSelectListItem[]),
			typeof(IEnumerable<ComboSelectListItem>)
		};

		private Configs _config;

		private string _gridIdUserSet;

		private string _id;

		/// <summary>
		/// 生成表格的列
		/// </summary>
		private List<List<LayuiColumn>> _layuiCols;

		private IBasePagedListVM<TopBasePoco, BaseSearcher> _listVM;
		private List<string> _merge;

		/// <summary>
		/// Grid 行内按钮
		/// </summary>
		private StringBuilder _rowBtnStrBuilder = new StringBuilder();

		private string _searchPanelId;
		private string _tableJSVar;

		/// <summary>
		/// Grid 工具条按钮
		/// </summary>
		private StringBuilder _toolBarBtnStrBuilder = new StringBuilder();

		/// <summary>
		/// wtm行事件switch处理函数的主体
		/// </summary>
		private StringBuilder _wtmgridBtnEventStr = new StringBuilder();

		private bool hasButtonGroup = false;

		private IBasePagedListVM<TopBasePoco, BaseSearcher> ListVM
		{
			get
			{
				if (_listVM == null)
				{
					_listVM = Vm?.Model as IBasePagedListVM<TopBasePoco, BaseSearcher>;
				}
				return _listVM;
			}
		}

		private string ToolBarId => $"{TABLE_TOOLBAR_PREFIX}{(string.IsNullOrEmpty(_gridIdUserSet) ? ListVM.UniqueId : _gridIdUserSet)}";

		#region 公开事件

		/// <summary>
		/// 工具栏事件处理完成后的回调
		/// 唯一参数:obj
		/// obj.data; 获得当前行数据
		/// obj.event; 获得 lay-event 对应的值
		/// obj.tr; 获得当前行 tr 的 DOM 对象（如果有的话）
		/// </summary>
		[HtmlAttributeName("after-toolevent-func")]
		public string AfterToolEventFunc { get; set; }

		/// <summary>
		/// 工具栏事件处理开始前的回调
		/// 如设定，返回true，则继续执行后面的函数
		/// 唯一参数:obj
		/// obj.data; 获得当前行数据
		/// obj.event; 获得 lay-event 对应的值
		/// obj.tr; 获得当前行 tr 的 DOM 对象（如果有的话）
		/// </summary>
		[HtmlAttributeName("before-toolevent-func")]
		public string BeforeToolEventFunc { get; set; }

		/// <summary>
		/// 复选框选中事件
		/// 点击复选框时触发，回调函数返回一个object参数，携带的成员如下：
		/// obj.checked //当前是否选中状态
		/// obj.data    //选中行的相关数据
		/// obj.type    //如果触发的是全选，则为：all，如果触发的是单选，则为：one
		/// </summary>
		[HtmlAttributeName("checked-func")]
		public string CheckedFunc { get; set; }

		/// <summary>
		/// 用户自定义工具栏事件处理函数
		/// 唯一参数:obj
		/// obj.data; 获得当前行数据
		/// obj.event; 获得 lay-event 对应的值
		/// obj.tr; 获得当前行 tr 的 DOM 对象（如果有的话）
		/// </summary>
		[HtmlAttributeName("custom-toolevent-func")]
		public string CustomToolEventFunc { get; set; }

		/// <summary>
		/// 数据渲染完的回调
		/// <para>无论是异步请求数据，还是直接赋值数据，都会触发该回调。你可以利用该回调做一些表格以外元素的渲染。</para>
		/// 重载参数为 obj
		/// { res: res, curr: curr, count: count, self: this, gridId: id }
		/// <para>obj.res:    如果是异步请求数据方式，res即为你接口返回的信息。</para>
		/// <para>        如果是直接赋值的方式，res即为：{data: [], count: 99} data为当前页数据、count为数据总长度</para>
		/// <para>obj.curr:   得到当前页码</para>
		/// <para>obj.count:  得到数据总量</para>
		/// obj.self 当前this
		/// obj.gridId table的id
		/// </summary>
		public string DoneFunc { get; set; }

		/// <summary>
		/// 行点击事件
		/// 唯一参数 obj
		/// obj.tr 得到当前行元素对象
		/// obj.data 得到当前行数据
		/// obj.del() 删除当前行
		/// obj.update(fields) 修改当前行数据
		/// </summary>
		[HtmlAttributeName("row-click-func")]
		public string RowClickFunc { get; set; }

		/// <summary>
		/// 行双击事件
		/// 唯一参数 obj
		/// obj.tr 得到当前行元素对象
		/// obj.data 得到当前行数据
		/// obj.del() 删除当前行
		/// obj.update(fields) 修改当前行数据
		/// </summary>
		[HtmlAttributeName("row-doubleclick-func")]
		public string RowDoubleClickFunc { get; set; }

		#endregion 公开事件

		#region 公开属性

		/// <summary>
		/// 单元格默认宽度
		/// </summary>
		[HtmlAttributeName("cell-defaultwidth")]
		public int CellDefaultWidth { get; set; } = 100;

		/// <summary>
		/// 复选框在第几列
		/// </summary>
		public int CheckboxIndex { get; set; }

		/// <summary>
		/// 直接赋值数据
		/// <para>你也可以对表格直接赋值，而无需配置异步数据请求接口。他既适用于只展示一页数据，也非常适用于对一段已知数据进行多页展示。</para>
		/// </summary>
		public ModelExpression Data { get; set; }

		/// <summary>
		/// 查询条件
		/// 只对url有效，使用本地数据时无效
		/// </summary>
		public Dictionary<string, object> Filter { get; set; }

		/// <summary>
		/// 设定容器高度 默认值：'auto' 若height>=0采用 '固定值' 模式，若height 小于 0 采用 'full-差值' 模式
		/// <para>固定值: 设定一个数字，用于定义容器高度，当容器中的内容超出了该高度时，会自动出现纵向滚动条</para>
		/// <para>full-差值: 高度将始终铺满，无论浏览器尺寸如何。这是一个特定的语法格式，其中 full 是固定的，而 差值 则是一个数值，这需要你来预估，比如：表格容器距离浏览器顶部和底部的距离“和” <para>
		/// </summary>
		public int? Height { get; set; }

		public string Id
		{
			get
			{
				if (string.IsNullOrEmpty(_id))
				{
					if (string.IsNullOrEmpty(_gridIdUserSet))
					{
						_id = $"{TABLE_ID_PREFIX}{ListVM.UniqueId}";
					}
					else
					{
						_id = _gridIdUserSet;
					}
				}
				return _id;
			}
			set
			{
				_id = value;
				_gridIdUserSet = value;
			}
		}

		/// <summary>
		/// 索引行的标题
		/// 默认为#
		/// </summary>
		[HtmlAttributeName("indexcolumn-title")]
		public string IndexColumnTitle { get; set; } = "#";

		/// <summary>
		/// 所有单元格居中
		/// 默认居中
		/// </summary>
		[HtmlAttributeName("is-allcell-center")]
		public bool IsAllCellCenter { get; set; } = true;

		/// <summary>
		/// 渲染后自动搜索数据
		/// </summary>
		[HtmlAttributeName("is-autosearch")]
		public bool IsAutoSearch { get; set; } = true;

		/// <summary>
		/// 全部选中
		/// </summary>
		[HtmlAttributeName("is-checkedall")]
		public bool? IsCheckedAll { get; set; }

		/// <summary>
		/// 列拖拽
		/// </summary>
		[HtmlAttributeName("is-dragcolumn")]
		public bool IsDragColumn { get; set; } = true;

		/// <summary>
		/// 行拖拽
		/// </summary>
		[HtmlAttributeName("is-dragrow")]
		public bool IsDragRow { get; set; }

		/// <summary>
		/// 可编辑
		/// </summary>
		public bool IsEditable { get; set; }

		/// <summary>
		/// 隔行背景，默认true
		/// <para>若不开启隔行背景，设置为false即可</para>
		/// </summary>
		public bool? Even { get; set; }

		/// <summary>
		/// 固定索引列
		/// </summary>
		[HtmlAttributeName("is-fixed-indexcolumn")]
		public bool IsFixedIndexColumn { get; set; }

		/// <summary>
		/// 隐藏表头工具栏
		/// </summary>
		[HtmlAttributeName("is-hide-panel")]
		public bool IsHidePanel { get; set; }

		/// <summary>
		/// 设定复选框列 默认false
		/// </summary>
		[HtmlAttributeName("is-hide-checkbox")]
		public bool IsHideCheckbox { get; set; }

		/// <summary>
		/// 设定复选框列 默认false
		/// </summary>
		[HtmlAttributeName("is-hide-indexcolumn")]
		public bool IsHideIndexColumn { get; set; }

		public bool IsInSelector { get; set; }

		/// <summary>
		/// 前端排序
		/// </summary>
		[HtmlAttributeName("is-localsort")]
		public bool IsLocalSort { get; set; }

		/// <summary>
		/// 设置单元格是否允许自动换行，默认为false
		/// </summary>
		[HtmlAttributeName("is-multiline")]
		public bool IsMultiLine { get; set; }

		/// <summary>
		/// 是否显示汇总行
		/// </summary>
		[HtmlAttributeName("is-showtotal")]
		public bool IsShowTotal { get; set; }

		/// <summary>
		/// 使用layui原始渲染，
		/// 加载后不修改高度，
		/// 不设置单元格格式
		/// </summary>
		[HtmlAttributeName("is-original")]
		public bool IsOriginal { get; set; }

		[HtmlAttributeName("is-showexports")]
		public bool? IsShowExports { get; set; }

		/// <summary>
		/// 是否显示删选列按钮
		/// </summary>
		[HtmlAttributeName("is-showfilter")]
		public bool? IsShowFilter { get; set; }

		/// <summary>
		/// 是否显示加载条 默认 true
		/// <para>如果设置 false，则在切换分页时，不会出现加载条。该参数只适用于“异步数据请求”的方式（即设置了url的情况下）</para>
		/// </summary>
		[HtmlAttributeName("is-showloading")]
		public bool IsShowLoading { get; set; } = true;

		/// <summary>
		/// 是否显示打印
		/// </summary>
		[HtmlAttributeName("is-showprint")]
		public bool? IsShowPrint { get; set; }

		/// <summary>
		/// 直接从 ListVM的EntityList获取数据
		/// </summary>
		[HtmlAttributeName("is-use-localdata")]
		public bool IsUseLocalData { get; set; }

		/// <summary>
		/// 默认每页数量 20
		/// </summary>
		public int? Limit { get; set; }

		/// <summary>
		/// 每页数据量可选项
		/// <para>默认值：[10, 20, 50, 80, 100, 150, 200]</para>
		/// </summary>
		public int[] Limits { get; set; }

		/// <summary>
		/// 行高
		/// </summary>
		public int? LineHeight { get; set; }

		/// <summary>
		/// 逗号分隔
		/// true为全选
		/// </summary>
		public string Merge { get; set; }

		/// <summary>
		/// Http Request Method 默认 GET
		/// <para>如果无需自定义HTTP类型，可不加该参数 <see cref="HttpMethodEnum" /></para>
		/// </summary>
		public HttpMethodEnum? Method { get; set; }

		/// <summary>
		/// 搜索面板 Id
		/// </summary>
		public string SearchPanelId
		{
			get
			{
				if (string.IsNullOrEmpty(_searchPanelId))
				{
					_searchPanelId = $"{FormTagHelper.FORM_ID_PREFIX}{ListVM.UniqueId}";
				}
				return _searchPanelId;
			}
			set
			{
				_searchPanelId = value;
			}
		}

		/// <summary>
		/// 用于设定表格尺寸，若使用默认尺寸不设置该属性即可
		/// </summary>
		public DataTableSizeEnum? Size { get; set; }

		/// <summary>
		/// 用于设定表格风格，若使用默认风格不设置该属性即可
		/// </summary>
		public DataTableSkinEnum? Skin { get; set; }

		/// <summary>
		/// 自定义插槽
		/// 如有引号，请使用单引号
		/// </summary>
		[Obsolete]
		public string Slot { get; set; }

		/// <summary>
		/// datatable 渲染之后返回对象的变量名
		/// </summary>
		[Obsolete]
		public string TableJSVar
		{
			get
			{
				if (string.IsNullOrEmpty(_tableJSVar))
				{
					_tableJSVar = $"{TABLE_JSVAR_PREFIX}{(string.IsNullOrEmpty(_gridIdUserSet) ? ListVM.UniqueId : _gridIdUserSet)}";
				}
				return _tableJSVar;
			}
		}

		/// <summary>
		/// layui表的名称
		/// </summary>
		public string Title { get; set; }

		/// <summary>
		/// 表头工具栏
		/// </summary>
		public string Toolbar { get; set; }

		/// <summary>
		/// 合计文字所在的列
		/// </summary>
		[HtmlAttributeName("totaltext-index")]
		public int TotalTextIndex { get; set; } = 2;

		/// <summary>
		/// 接口地址
		/// </summary>
		public string Url { get; set; }

		public ModelExpression Vm { get; set; }

		/// <summary>
		/// VM
		/// </summary>
		[Obsolete]
		public Type VMType { get; set; }

		/// <summary>
		/// 设定容器宽度 默认值：'auto'
		/// table容器的默认宽度是 auto，你可以借助该参数设置一个固定值，当容器中的内容超出了该宽度时，会自动出现横向滚动条。
		/// </summary>
		public int? Width { get; set; }

		#endregion 公开属性

		void SetLayuiPage()
		{
			if (IsNeedPage == null)
			{
				IsNeedPage = ListVM.NeedPage;
			}
		}
		public override void Process(TagHelperContext context, TagHelperOutput output)
		{
			if (ListVM == null)
			{
				throw new NullReferenceException("vm不能为null");
			}


			UseCascadeParameter<TreeContainerTagHelper>(context, x =>
			{
				x.SetDictValue("grid-id", Id);

				//如果在treeContainer中则默认关闭自动加载数据,由treeContainer来决定第一次是否加载
				IsAutoSearch = false;
			});

			output.TagName = "table";
			output.Attributes.Add("id", Id);

			output.Attributes.Add("lay-filter", Id);
			output.Attributes.Add("subpro", ListVM.DetailGridPrix);
			output.TagMode = TagMode.StartTagAndEndTag;

			_config = ListVM.ConfigInfo;
			var vmQualifiedName = Vm.Model.GetType().AssemblyQualifiedName;
			vmQualifiedName = vmQualifiedName.Substring(0, vmQualifiedName.LastIndexOf(", Version=", StringComparison.CurrentCulture));
			var maxDepth = (ListVM?.GetChildrenDepth()) ?? 1;


			SetLayuiPage();
			SetTitle();
			SetMutilline();
			SetPageMode(vmQualifiedName);
			SetLayuiColumns(maxDepth);
			SetTableButton(vmQualifiedName);

			//处理data
			var dataObj = GetLocalData();

			output.PostElement.AppendHtml($@"
<script>
wtm.datatable_Render({{
	id:'{Id}',
	beforeMount:'{BeforeMount}',
	mounted:'{Mounted}',
	autoSearch:{GetBooleanString(IsAutoSearch)},
	autoSort:{GetAutoSortString()},
	cellMinWidth:{CellDefaultWidth},
	checkedFunc:'{CheckedFunc}',
	cols:{GetLayuiCols()},
	data:{dataObj.html},
	defaultToolbar:{GetDefaultToolbar()}, 
	detailGridPrix:{string.IsNullOrEmpty(ListVM.DetailGridPrix).ToString().ToLower()},
	doneFunc:'{DoneFunc}',
	drag:{GetDragColumnString()},
	escape:false,
	even:{(Even.HasValue && !Even.Value ? "false" : "true")},
	fieldPre:'{GetFieldPre()}',
	headerHeight:{maxDepth * (Size == DataTableSizeEnum.SM || IsInSelector ? 31 : 38) + 0},
	height: {(!Height.HasValue ? "null" : (Height.Value >= 0 ? $"{Height.Value}" : $"'full{Height.Value}'"))},
	isOriginal:{GetBooleanString(IsOriginal)},
	limit:{(IsNeedPage == true ? $"{GetLimit()}" : $"{(IsUseLocalData ? ListVM.GetEntityList().Count().ToString() : "0")}")},
	limits:{(IsNeedPage == true ? GetLimits() : "null")},
	loading:{GetBooleanString(IsShowLoading)},
	method: {(Method == null ? "'post'" : $"'{Method.Value.ToString().ToLower()}'")},
	multiLine:{GetBooleanString(IsMultiLine)},
	request:{(IsInSelector == false ? $"{{ 'pageName': 'Page', 'limitName': 'Limit'}}" : "")},
	rowClickFunc:'{RowClickFunc}',
	rowDoubleClickFunc:'{RowDoubleClickFunc}',
	rowDrag:{GetBooleanString(IsDragRow)},
	searchPanelId:'{SearchPanelId}',
	size:{(!Size.HasValue ? "null" : $"'{Size.Value.ToString().ToLower()}'")},
	skin:{(!Skin.HasValue ? "null" : $"'{Skin.Value.ToString().ToLower()}'")},
	sysError:'{THProgram._localizer["Sys.Error"]}',
	sysFilter:'{THProgram._localizer["Sys.ColumnFilter"]}',
	sysPrint:'{THProgram._localizer["Sys.Print"]}',
	text:{{none:'{THProgram._localizer["Sys.NoData"]}'}},
	title:'{Title}',
	toolbar:{GetToolbar(_toolBarBtnStrBuilder)},
	totalRow:{(!IsShowTotal ? "false" : "true")},
	url:'{Url}',
	where:{(Filter == null || Filter.Count == 0 ? "null" : $"{JsonSerializer.Serialize(Filter)}")},
	width:{(!Width.HasValue ? "null" : $"{Width.Value}")},
	wtToolBarFunc:wtm.table_GetwtToolBarFunc(""{_wtmgridBtnEventStr}"",'{CustomToolEventFunc}','{BeforeToolEventFunc}','{AfterToolEventFunc}'),
	page:{(IsNeedPage == true ? $@"{{
			rpptext:'{THProgram._localizer["Sys.RecordsPerPage"]}',
			totaltext:'{THProgram._localizer["Sys.Total"]}',
			recordtext:'{THProgram._localizer["Sys.Record"]}',
			gototext:'{THProgram._localizer["Sys.Goto"]}',
			pagetext:'{THProgram._localizer["Sys.Page"]}',
			oktext:'{THProgram._localizer["Sys.GotoButtonText"]}'}}" : "false")},
}});

</script>

<template id='{ToolBarId}2'>
	<div id='{Id}buttons' >
	{_toolBarBtnStrBuilder}
	</div>
</template>

<template id='{ToolBarId}'>
	{_rowBtnStrBuilder}
</template>

{dataObj.script}
");

			output.PostElement.AppendHtml($@"
{(string.IsNullOrEmpty(ListVM.DetailGridPrix) ? string.Empty : $"<input type='hidden' name='{Vm.Name}.DetailGridPrix' value='{ListVM.DetailGridPrix}'/>")}
");
			base.Process(context, output);
		}

		#region Get

		/// <summary>
		/// 添加按钮
		/// </summary>
		/// <param name="vmQualifiedName"></param>
		/// <param name="rowBtnStrBuilder"></param>
		/// <param name="toolBarBtnStrBuilder"></param>
		/// <param name="gridBtnEventStrBuilder"></param>
		/// <param name="vm"></param>
		/// <param name="item"></param>
		/// <param name="isSub"></param>
		private void AddSubButton(string vmQualifiedName, StringBuilder rowBtnStrBuilder, StringBuilder toolBarBtnStrBuilder, StringBuilder gridBtnEventStrBuilder, BaseVM vm, GridAction item, bool isSub = false)
		{
			if (vm.Wtm?.IsAccessable(item.Url) == true || item.ParameterType == GridActionParameterTypesEnum.AddRow || item.ParameterType == GridActionParameterTypesEnum.RemoveRow)
			{
				// Grid 行内按钮
				if (item.ShowInRow)
				{
					if (item.ParameterType != GridActionParameterTypesEnum.RemoveRow)
					{
						bool condition = false || string.IsNullOrEmpty(item.BindVisiableColName) == false;
						if (condition == true)
						{
							rowBtnStrBuilder.Append("{{#  if(d." + item.BindVisiableColName + " == true || d." + item.BindVisiableColName + ".toLowerCase() == 'true' ){ }}");
						}
						rowBtnStrBuilder.Append($@"<a class='wtm-row-btn layui-btn {(string.IsNullOrEmpty(item.ButtonClass) ? "layui-btn-primary" : $"{item.ButtonClass}")} layui-btn-xs' lay-event='{item.Area + item.ControllerName + item.ActionName + item.QueryString}'>{item.Name}</a>");
						if (condition == true)
						{
							rowBtnStrBuilder.Append("{{#  } else{ }}");
							rowBtnStrBuilder.Append("{{# } }}");
						}
					}
					else
					{
						rowBtnStrBuilder.Append($@"<a class='wtm-row-btn layui-btn {(string.IsNullOrEmpty(item.ButtonClass) ? "layui-btn-primary" : $"{item.ButtonClass}")} layui-btn-xs' onclick=""ff.RemoveGridRow('{Id}',{{{{d.LAY_INDEX}}}})"">{item.Name}</a>");
					}
				}

				// Grid 工具条按钮 添加行工具条按钮
				if (!item.HideOnToolBar)
				{
					var icon = $@"<i class='{item.IconCls}'></i>";

					//如果是按钮组容器，加载子按钮
					if (item.ActionName?.Equals("ActionsGroup") == true && item.SubActions != null && item.SubActions.Count > 0)
					{
						var subBarBtnStrList = new StringBuilder();
						foreach (var subItem in item.SubActions)
						{
							StringBuilder subBarBtnStr = new StringBuilder();
							AddSubButton(vmQualifiedName, rowBtnStrBuilder, subBarBtnStr, gridBtnEventStrBuilder, vm, subItem, true);
							if (subBarBtnStr.Length > 0)
							{
								subBarBtnStrList.AppendFormat("<dd style='padding: 0;margin-top:1px;line-height: initial;'>{0}</dd>", subBarBtnStr.ToString());
							}
						}
						if (subBarBtnStrList.Length == 0)
						{
							return;
						}
						toolBarBtnStrBuilder.Append($@"<button class='layui-btn {(string.IsNullOrEmpty(item.ButtonClass) ? "layui-btn-normal" : $"{item.ButtonClass}")} layui-btn-xs layui-unselect layui-form-select downpanel' style='opacity:1;' id='btn_{item.ButtonId}'>
                                 <div class='layui-select-title' style='padding-right:20px;'>
                                        {item.Name}
                                 <i class='layui-edge'></i>
                                 </div> 
                                 <dl class='layui-anim layui-anim-upbit' style='top: initial;padding:0;border:none;z-index:3;' >
                                    {subBarBtnStrList}
                                 </dl></button>
                                ".Replace("\r\n", ""));
						hasButtonGroup = true;

						//按钮组时直接返回
						return;
					}
					else
					{
						string substyle = isSub ? "style='width:100%;'" : "";
						string eventName = (item.Area + item.ControllerName + item.ActionName + item.QueryString) ?? "";
						toolBarBtnStrBuilder.Append($"<a lay-event='{eventName}' class='wtm-tool-btn layui-btn {(string.IsNullOrEmpty(item.ButtonClass) ? "layui-btn-normal" : $"{item.ButtonClass}")} layui-btn-xs' {substyle}>{icon}{item.Name}</a>");
					}
				}
				var url = item.Url;
				if (item.ControllerName == "_Framework" && item.ActionName == "GetExportExcel") // 导出按钮 接口地址
				{
					url = $"{url}&_DONOT_USE_VMNAME={vmQualifiedName}";
				}

				// 处理行工具栏事件
				var script = new StringBuilder($"var tempUrl = '{url}',whereStr={JsonSerializer.Serialize(item.whereStr)};");
				switch (item.ParameterType)
				{
					case GridActionParameterTypesEnum.NoId: break;
					case GridActionParameterTypesEnum.SingleId:
						script.Append($@"
var rs=wtm.table_SingleId('{Id}',tempUrl,whereStr,'{THProgram._localizer["Sys.SelectOneRow"]}','{THProgram._localizer["Sys.SelectOneRowMax"]}',data);
if(rs===false) break;
ids=rs.ids;tempUrl=rs.tempUrl;
".Replace("\r\n", ""));
						break;

					case GridActionParameterTypesEnum.MultiIds:
						script.Append($@"
var rs=wtm.table_MultiIds('{Id}','{THProgram._localizer["Sys.SelectOneRowMin"]}');
if(rs===false) break;
ids=rs.ids;isPost=rs.isPost;
".Replace("\r\n", ""));
						break;

					case GridActionParameterTypesEnum.SingleIdWithNull:
						script.Append($@"
var rs=wtm.table_SingleIdWithNull('{Id}',tempUrl,whereStr,'{THProgram._localizer["Sys.SelectOneRowMax"]}',data);
if(rs===false) break;
ids=rs.ids;tempUrl=rs.tempUrl;
".Replace("\r\n", ""));
						break;

					case GridActionParameterTypesEnum.MultiIdWithNull:
						script.Append($@"ids = ff.GetSelections('{Id}');isPost = true;");
						break;

					default: break;
				}

				gridBtnEventStrBuilder.Append($@"
case '{item.Area + item.ControllerName + item.ActionName + item.QueryString}':{{");
				if (item.ParameterType == GridActionParameterTypesEnum.AddRow)
				{
					Regex r = new Regex("<script>.*?</script>");
					gridBtnEventStrBuilder.Append($@"ff.AddGridRow('{Id}',{r.Replace(ListVM.GetSingleDataJson(null, false), "")});");
				}
				else if (item.ParameterType == GridActionParameterTypesEnum.RemoveRow) { }
				else
				{
					string actionScript = "";
					if (string.IsNullOrEmpty(item.OnClickFunc))
					{
						if (item.IsDownload == true)
						{
							actionScript = $"ff.Download(tempUrl,ids);";
						}
						else if (item.ShowDialog == true)
						{
							string width = "null";
							string height = "null";
							if (item.DialogWidth != null)
							{
								width = item.DialogWidth.ToString();
							}
							if (item.DialogHeight != null)
							{
								height = item.DialogHeight.ToString();
							}
							if (item.IsRedirect == true)
							{
								actionScript = $"ff.LoadPage(tempUrl,{item.IsRedirect.ToString().ToLower()},'{item.DialogTitle ?? ""}',isPost===true&&ids!==null&&ids!==undefined?{{'Ids':ids}}:undefined);";
							}
							else
							{
								actionScript = $"ff.OpenDialog(tempUrl,'{Guid.NewGuid().ToNoSplitString()}','{item.DialogTitle}',{width},{height},isPost===true&&ids!==null&&ids!==undefined?{{'Ids':ids}}:undefined,{item.Max.ToString().ToLower()});";
							}
						}
						else
						{
							if ((item.Area == string.Empty && item.ControllerName == "_Framework" && item.ActionName == "GetExportExcel") || item.IsExport == true)
							{
								actionScript = $"ff.DownloadExcelOrPdf(tempUrl,'{SearchPanelId}',{{}},ids);";
							}
							else
							{
								if (item.IsRedirect == true)
								{
									actionScript = $"ff.LoadPage(tempUrl,false,'{item.DialogTitle ?? ""}',isPost===true&&ids!==null&&ids!==undefined?{{'Ids':ids}}:undefined);";
								}
								else
								{
									if (item.ForcePost == true)
									{
										actionScript = $"ff.BgRequest(tempUrl, ids!==null&&ids!==undefined?{{'Ids':ids}}:undefined);";
									}
									else
									{
										actionScript = $"ff.BgRequest(tempUrl, isPost===true&&ids!==null&&ids!==undefined?{{'Ids':ids}}:undefined);";
									}
								}
							}
						}
					}
					else
					{
						if (item.ShowInRow)
						{
							actionScript = $"{item.OnClickFunc}({{ids:ids,data:data,tr:tr,gridId:'{Id}'}});";
						}
						else
						{
							actionScript = $"{item.OnClickFunc}({{ids:ids,data:ff.GetSelectionData('{Id}'),tr:tr,gridId:'{Id}'}});";
						}
					}
					if (string.IsNullOrEmpty(item.PromptMessage) == false)
					{
						actionScript = $@"
        layer.confirm('{item.PromptMessage}', {{title:'{THProgram._localizer["Sys.Info"]}'}},function(index){{
            {actionScript}
        layer.close(index);
      }});".Replace("\r\n", "");
					}

					gridBtnEventStrBuilder.Append($@"
var isPost = false;
{script}
{actionScript}".Replace("\r\n", ""));
				}
				gridBtnEventStrBuilder.Append($@"}};break;");
			}
		}

		/// <summary>
		/// 生成layui table的列
		/// </summary>
		/// <param name="rawCols"></param>
		/// <param name="nextCols"></param>
		/// <param name="tempCols">子列</param>
		/// <param name="maxDepth"></param>
		/// <param name="depth"></param>
		private void generateColHeaderCore(IEnumerable<IGridColumn<TopBasePoco>> rawCols, List<IGridColumn<TopBasePoco>> nextCols, List<LayuiColumn> tempCols, int maxDepth, int depth)
		{
			string random = Guid.NewGuid().ToString().Replace("-", "");
			//处理列
			foreach (var item in rawCols)
			{
				var tempCol = new LayuiColumn()
				{
					Title = item.Title,
					Field = item.Field,
					Width = item.Width,
					Sort = item.Sort,
					Fixed = item.Fixed,
					Align = IsAllCellCenter ? GridColumnAlignEnum.Center : item.Align,
					Event = item.Event,
					UnResize = item.UnResize,
					Hide = item.Hide,
					//EditType = Editable ? EditTypeEnum.Text : null,
					Merge = GetMerge(item.Field),
					ShowTotal = item.ShowTotal == true ? "{{ wtm.datatable_TotalTextTemplate(d.TOTAL_NUMS) }}" : null //对整数进行格式化
				};

				if (LineHeight != null && item.Fixed.HasValue)
				{
					tempCol.Style = $"height:{LineHeight}px";
				}

				// 非编辑状态且有字段名的情况下，设置template
				if ((string.IsNullOrEmpty(ListVM.DetailGridPrix) == true && string.IsNullOrEmpty(item.Field) == false) || item.Field == "BatchError")
				{
					if (!IsOriginal)
					{
						tempCol.Templet = GetCellTemplate(item.Field, random);
					}
				}

				IsShowTotal |= item.ShowTotal == true;
				switch (item.ColumnType)
				{
					case GridColumnTypeEnum.Space:
						tempCol.Type = LayuiColumnTypeEnum.Space;
						break;

					case GridColumnTypeEnum.Action:
						tempCol.Toolbar = $"#{ToolBarId}";
						break;
				}
				if (item.Children != null && item.Children.Any())
				{
					tempCol.Colspan = item.ChildrenLength;
				}
				if (maxDepth > 1 && (item.Children == null || !item.Children.Any()))
				{
					if (maxDepth - depth > 1)
					{
						tempCol.Rowspan = maxDepth - depth;
					}
				}
				tempCols.Add(tempCol);
				if (item.Children != null && item.Children.Any())
					nextCols.AddRange(item.Children);
			}
		}

		private string GetAutoSortString()
		{
			if (IsLocalSort)
			{
				return "true";
			}
			return "false";
		}

		private string GetCellTemplate(string field, string random)
		{
			return $"wtm.table_GetTemplate('{field}','{random}')";
		}

		private string GetDefaultToolbar()
		{
			var config = ListVM.ConfigInfo;
			StringBuilder builder = new StringBuilder();
			var exp_config = "";
			config?.AppSettings.TryGetValue("ShowExports", out exp_config);
			var export = IsShowExports ?? (exp_config == "true");

			var filter = IsShowFilter ?? config?.UIOptions.DataTable.ShowFilter ?? false;
			var print = IsShowPrint ?? config?.UIOptions.DataTable.ShowPrint ?? false;

			if (export) builder.Append("'exports',");
			if (filter) builder.Append("'filter',");
			if (print) builder.Append("'print',");

			if (builder.Length > 0)
			{
				return $"[{builder}]";
			}
			else
			{
				return "[]";
			}
		}

		/// <summary>
		/// 获取拖拽列的配置
		/// </summary>
		/// <returns></returns>
		private string GetDragColumnString()
		{
			return IsDragColumn ? "'simple'" : "false";
		}

		private string GetFieldPre()
		{
			if (IsInSelector)
			{
				return Vm.Name;
			}

			StringBuilder rv = new StringBuilder("Searcher");
			if (!string.IsNullOrEmpty(Vm?.Name))
			{
				rv.Insert(0, Vm.Name);
			}
			return rv.ToString();
		}

		/// <summary>
		/// 处理表格列数据中的script
		/// 防止就近闭合 导致js报错
		/// </summary>
		/// <returns></returns>
		private (string html, string script) GetLocalData()
		{
			if (IsUseLocalData)
			{
				var data = ListVM.GetDataJson();
				var regex = new Regex("<script>.*?</script>");
				var scripts = regex.Matches(data);
				StringBuilder script = new StringBuilder();
				foreach (Match item in scripts)
				{
					script.Append(item.Value);
				}
				string html = regex.Replace(data, "");
				return (html, script.ToString());
			}
			else
			{
				return ("[]", "");
			}
		}

		private string GetLayuiCols()
		{
			return JsonSerializer.Serialize(_layuiCols, JsonOptions).Replace("\"_raw_", "").Replace("_raw_\"", "").Replace("\\r\\n", "").Replace("\\\"", "\"");
		}

		private int GetLimit()
		{
			if (Limit == null)
			{
				Limit = _config?.UIOptions.DataTable.RPP;
			}
			return Limit ?? 0;
		}

		private string GetLimits()
		{
			if (Limits == null)
			{
				Limits = new int[] { 10, 20, 50, 80, 100, 150, 200 };
				if (!Limits.Contains(Limit.Value))
				{
					var list = Limits.ToList();
					list.Add(Limit.Value);
					Limits = list.OrderBy(x => x).ToArray();
				}
			}

			if (Limits.Length == 0)
			{
				return "null";
			}
			else
			{
				return $"[{string.Join(',', Limits)}]";
			}
		}

		/// <summary>
		/// 获取单元格合并配置
		/// </summary>
		/// <param name="field"></param>
		/// <returns></returns>
		private string GetMerge(string field)
		{
			if (string.IsNullOrEmpty(Merge))
			{
				return null;
			}

			if (Merge == "true")
			{
				return field;
			}

			if (_merge == null)
			{
				_merge = Merge.Split(',').ToList();
			}

			return _merge.Contains(field) ? field : null;
		}

		private string GetToolbar(StringBuilder builder)
		{
			if (IsHidePanel)
			{
				return "false";
			}

			if (!string.IsNullOrEmpty(Toolbar))
			{
				builder.Append(Toolbar);
				return $"'{Toolbar}'";
			}

			if (builder.Length > 0 || GetDefaultToolbar().Length > 0)
			{
				return $"'#{ToolBarId}2'";
			}

			return "false";
		}

		#endregion

		#region Set

		private void SetLayuiColumns(int maxDepth)
		{
			var rawCols = ListVM?.GetHeaders();
			var tempCols = new List<LayuiColumn>();
			// 添加复选框
			if (!IsHideCheckbox)
			{
				var checkboxHeader = new LayuiColumn()
				{
					Type = LayuiColumnTypeEnum.Checkbox,
					LAY_CHECKED = IsCheckedAll,
					Rowspan = maxDepth,
					//Fixed = GridColumnFixedEnum.Left,
					UnResize = true,
					Width = 30
				};
				if (LineHeight != null)
				{
					checkboxHeader.Style = $"height:{LineHeight}px";
				}
				tempCols.Add(checkboxHeader);
			}
			// 添加序号列
			if (!IsHideIndexColumn)
			{
				var gridIndex = new LayuiColumn()
				{
					Type = LayuiColumnTypeEnum.Numbers,
					Rowspan = maxDepth,
					Fixed = IsFixedIndexColumn ? GridColumnFixedEnum.Left : null,
					UnResize = true,
					Title = IndexColumnTitle,
					Width = 35
				};
				if (LineHeight != null)
				{
					gridIndex.Style = $"height:{LineHeight}px";
				}
				tempCols.Add(gridIndex);
			}
			var nextCols = new List<IGridColumn<TopBasePoco>>();// 下一级列头

			generateColHeader(rawCols, nextCols, tempCols, maxDepth, 0);

			_layuiCols.Add(tempCols);

			if (nextCols.Count > 0)
			{
				CalcChildCol(_layuiCols, nextCols, maxDepth, 1);
			}

			if (_layuiCols.Count > 0 && _layuiCols[0].Count > 0)
			{
				var index = TotalTextIndex >= _layuiCols[0].Count ? 0 : TotalTextIndex;
				_layuiCols[0][index].TotalRowText = ListVM?.TotalText;
			}

			void CalcChildCol(List<List<LayuiColumn>> layuiCols, List<IGridColumn<TopBasePoco>> rawCols, int maxDepth, int depth)
			{
				var tempCols = new List<LayuiColumn>();
				layuiCols.Add(tempCols);

				var nextCols = new List<IGridColumn<TopBasePoco>>();// 下一级列头

				generateColHeader(rawCols, nextCols, tempCols, maxDepth, depth);

				if (nextCols.Count > 0)
				{
					CalcChildCol(layuiCols, nextCols, maxDepth, depth + 1);
				}
			}

			void generateColHeader(IEnumerable<IGridColumn<TopBasePoco>> rawCols, List<IGridColumn<TopBasePoco>> nextCols, List<LayuiColumn> tempCols, int maxDepth, int depth)
			{
				var temp = rawCols.Where(x => x.Fixed == GridColumnFixedEnum.Left).ToArray();
				generateColHeaderCore(temp, nextCols, tempCols, maxDepth, depth);

				temp = rawCols.Where(x => x.Fixed == null).ToArray();
				generateColHeaderCore(temp, nextCols, tempCols, maxDepth, depth);

				temp = rawCols.Where(x => x.Fixed == GridColumnFixedEnum.Right).ToArray();
				generateColHeaderCore(temp, nextCols, tempCols, maxDepth, depth);
			}
		}

		private void SetMutilline()
		{
			if (LineHeight.HasValue)
			{
				IsMultiLine = false;
			}
		}

		private void SetPageMode(string vmQualifiedName)
		{
			if (IsUseLocalData) // 不需要分页
			{
				//使用本地数据的时候，仍然需要分页显示
				//ListVM.NeedPage = false;
			}
			else
			{
				if (Filter == null) Filter = new Dictionary<string, object>();
				Filter.Add("_DONOT_USE_VMNAME", vmQualifiedName);
				Filter.Add("_DONOT_USE_CS", ListVM.CurrentCS);
				Filter.Add("SearcherMode", ListVM.SearcherMode);
				Filter.Add("SelectorValueField", ListVM.SelectorValueField);
				Filter.Add("ViewDivId", ListVM.ViewDivId);
				if (ListVM.Ids != null && ListVM.Ids.Count > 0)
				{
					Filter.Add("Ids", ListVM.Ids);
				}
				// 为首次加载添加Searcher查询参数
				if (ListVM.Searcher != null)
				{
					var props = ListVM.Searcher.GetType().GetAllProperties();
					props = props.Where(x => !_excludeTypes.Contains(x.PropertyType)).ToList();
					foreach (var prop in props)
					{
						if (!_excludeParams.Contains(prop.Name))
						{
							if (prop.PropertyType.IsGenericType == false || (prop.PropertyType.GenericTypeArguments[0] != typeof(ComboSelectListItem) && prop.PropertyType.GenericTypeArguments[0] != typeof(TreeSelectListItem)))
							{
								var listvalue = prop.GetValue(ListVM.Searcher);
								if (listvalue != null)
								{
									if (IsInSelector == true)
									{
										Filter.Add($"Searcher.{prop.Name}", prop.GetValue(ListVM.Searcher));
									}
									else
									{
										Filter.Add($"{prop.Name}", prop.GetValue(ListVM.Searcher));
									}
								}
							}
						}
					}
				}
			}
		}

		private void SetTableButton(string vmQualifiedName)
		{
			var actionCol = ListVM?.GetGridActions();

			var gridBtnEventStrBuilder = new StringBuilder();// Grid 按钮事件

			if (actionCol != null && actionCol.Count > 0)
			{
				var vm = Vm.Model as BaseVM;
				foreach (var item in actionCol)
				{
					AddSubButton(vmQualifiedName, _rowBtnStrBuilder, _toolBarBtnStrBuilder, gridBtnEventStrBuilder, vm, item);
				}
			}
			if (hasButtonGroup == true)
			{
				_toolBarBtnStrBuilder.Append($@"<script type='text/javascript' des='buttongroup'>wtm.table_ToolBarBtnGroup();</script>");
			}

			_wtmgridBtnEventStr = gridBtnEventStrBuilder.Replace("\r\n", "").Replace("\"", "\\\"");//wtm行事件switch处理函数的主体
		}

		private void SetTitle()
		{
			Title = Title ?? Vm.Model.GetType().Name.Replace("VM", "表");
		}

		#endregion
	}
}